/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Windows Forms Controls Class Library
 * Version 2.00
 * 
 * Gradient Progress Bar Control Class
 *      A progress bar control with a color gradient
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.Text;
using System.ComponentModel;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace Adaptive.Foundation.Windows.Forms
{
    /// <summary>
    /// Progress Bar Control which displays progress as a color gradient shift.
    /// </summary>
    /// <remarks>
    /// This progress bar control displays progress with a solid rectangle with the
    /// background color of the percentage value painted as a color gradient fade
    /// from the "start" color to the "end" color.
    /// </remarks>
    [Browsable(true), ToolboxItem(true), DesignerCategory("ProgressBar")]
    public partial class GradientProgressBar : AdaptiveControlBase
    {
        /*--------------------------------------------------------------------------------
         * Public Event Definitions
         *------------------------------------------------------------------------------*/
        #region Public Event Definitions
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Border style.
        /// </summary>
        private PanelBorderStyle _borderStyle = PanelBorderStyle.Inset;
        /// <summary>
        /// Gradient drawing style.
        /// </summary>
        private BarStyle _gradientStyle = BarStyle.FlatGradient;
        /// <summary>
        /// Percentage value.
        /// </summary>
        private int _percentComplete;
        /// <summary>
        /// Starting color value.
        /// </summary>
        private Color _startColor = SystemColors.Highlight;
        /// <summary>
        /// Ending color value.
        /// </summary>
        private Color _endColor = SystemColors.ActiveCaption;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public GradientProgressBar()
            : base()
        {
			InitializeComponent();

			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			SetStyle(ControlStyles.UserPaint, true);
			SetStyle(ControlStyles.ResizeRedraw, true);
            DoubleBuffered = true;

        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                    components.Dispose();
            }
            components = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets or sets the starting <see cref="Color"/> of the gradient progress indicator.
        /// </summary>
        /// <value>
        /// The starting <see cref="Color"/> the progress bar will be painted with
        /// on the left side.
        /// </value>
        /// <seealso cref="EndColor"/>
        [Browsable(true), Category("Apperance"),
        Description("Gets or sets the starting color of the gradient progress indicator.")]
        public Color StartColor
        {
            get { return _startColor; }
            set
            {
                _startColor = Color.FromArgb(155, value.R, value.G, value.B);
                Invalidate();
            }
        }
        /// <summary>
        /// Gets or sets the ending <see cref="Color"/> of the gradient progress indicator.
        /// </summary>
        /// <value>
        /// The ending <see cref="Color"/> the progress bar will be painted with
        /// on the right side.
        /// </value>
        /// <seealso cref="StartColor"/>
        [Browsable(true), Category("Apperance"),
        Description("Gets or sets the ending color of the gradient progress indicator.")]
        public Color EndColor
        {
            get { return _endColor; }
            set
            {
                _endColor = Color.FromArgb(155, value.R, value.G, value.B);
                Invalidate();
            }
        }
        /// <summary>
        /// Gets or sets the border style of the progress bar control.
        /// </summary>
        /// <value>
        /// A <see cref="PanelBorderStyle"/> value which indicates what type of 
        /// border is drawn, if any.
        /// </value>
        [Browsable(true), Category("Apperance"),
        Description("Gets or sets the border style of the progress bar control.")]
        public new PanelBorderStyle BorderStyle
        {
            get { return _borderStyle; }
            set
            {
                _borderStyle = value;
                Invalidate();
            }
        }
        /// <summary>
        /// Gets or sets the current position of the progress bar.
        /// </summary>
        /// <value>
        /// The position of the progress bar, as a percentage value.  The minimum
        /// value is zero (0), and the maximum value is 100.
        /// </value>
        /// <remarks>
        /// Setting this property to a value less than zero will result in the property
        /// being set to zero (0); setting this property to a value greater than 100 will
        /// result in the property being set to 100.
        /// </remarks>
        [Browsable(true), Category("Apperance"),
        Description("Gets or sets the current position of the progress bar.  The minimum value is zero (0), the maximum value is 100.")]
        public int Value
        {
            get { return _percentComplete; }
            set
            {
                if (value != _percentComplete)
                {

                    if (value < 0)
                        value = 0;
                    else if (value > 100)
                        value = 100;
                    _percentComplete = value;
                    Invalidate();
                    Update();
                }
            }
        }
        /// <summary>
        /// Gets or sets the style of gradient drawn in the progress bar.
        /// </summary>
        /// <value>
        /// A <see cref="BarStyle"/> enumerated value.
        /// </value>
        [Browsable(true), Category("Apperance"),
        Description("Gets or sets the style of the progress bar.")]
        public BarStyle GradientBarStyle
        {
            get { return _gradientStyle; }
            set
            {
                _gradientStyle = value;
                Invalidate();
            }
        }
        /// <summary>
        /// Gets or sets the internal Text property.
        /// </summary>
        /// <value>
        /// Text value.
        /// </value>
        [Browsable(false)]
        internal new string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Paints the background of the control.
        /// </summary>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //Raise base event.
            base.OnPaintBackground(e);

            if (Visible)
            {
                try
                {
                    DrawBorder(e.Graphics);
                }
                catch
                { }
            }
        }
        /// <summary>
        /// Paints the control and raises the 
        /// <see cref="System.Windows.Forms.Control.Paint"/> event.
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (Visible)
                Render(e.Graphics);

            base.OnPaint(e);
        }
        /// <summary>
        /// Redraws the control when resized, and raises the 
        /// <see cref="System.Windows.Forms.Control.Resize"/> event.
        /// </summary>
        protected override void OnResize(EventArgs e)
        {
            if (Visible)
                Refresh();

            base.OnResize(e);
        }
        /// <summary>
        /// This method is invoked in the main UI thread to perform any neccessary
        /// updates to the UI.
        /// </summary>
        /// <remarks>
        /// Calling the related <see cref="AdaptiveControlBase.UpdateControl()"/> is used to prevent illegal 
        /// cross-threaded UI update calls.
        /// </remarks>
        protected override void UpdateCurrentControlContents()
        {
            Invalidate();
            Refresh();
        }
        /// <summary>
        /// This method is invoked in the main UI thread to perform any neccessary
        /// updates to the UI.
        /// </summary>
        /// <remarks>
        /// Calling the related <see cref="AdaptiveControlBase.UpdateControl(object[])"/> is used to prevent illegal 
        /// cross-threaded UI update calls.
        /// </remarks>
        /// <param name="data">
        /// The data supplied to the control to update the internal controls with.
        /// </param>
        protected override void UpdateCurrentControlContents(object[] data)
        {
            Invalidate();
            Refresh();
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Renders the control using the specified graphics object using the 
        /// double-buffering technique.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> object used to draw on
        /// the control.</param>
        private void Render(Graphics g)
        {
            Bitmap renderedImage = null;			//Image to be displayed.
            Point borderPoint;

            if ((Visible) && (_percentComplete > 0))
            {
                try
                {
                    //Get the control's appearance image.
                    renderedImage = RenderAppearance();

                    //Get the start point of the control border, if present.
                    borderPoint = GetBorderPoint();

                    //Draw the control.
                    g.DrawImageUnscaled(renderedImage, new Point(1, 1));

                    //Clear memory.
                    renderedImage.Dispose();
                }
                catch
                {
                }
            }

            renderedImage = null;
        }
        /// <summary>
        /// Renders the control's appearance to a bitmap for use with the 
        /// double-buffering teachnique.
        /// </summary>
        /// <returns>
        /// A <see cref="Bitmap"/> image containing the image to be displayed.
        /// </returns>
        private Bitmap RenderAppearance()
        {
            Bitmap returnValue = null;			//Return value.
            Graphics localGraphics = null;		//Bitmap graphics object.
            int borderDepth = 0;				//Get border depth.
            int imageWidth = 0;					//Image width.
            int imageHeight = 0;				//Image height.

            //Calculate image size.
            borderDepth = GetBorderDepth();
            imageWidth = Width - (borderDepth * 2);
            imageHeight = Height - (borderDepth * 2);

            //Create the return bitmap and local graphics objects.
            returnValue = new Bitmap(imageWidth, imageHeight);
            localGraphics = Graphics.FromImage(returnValue);

            //Draw the control.
            DrawGradient(localGraphics, borderDepth, borderDepth, imageWidth, imageHeight);

            //Clear memory and return the bitmap.
            localGraphics.Dispose();
            localGraphics = null;

            return returnValue;

        }
        /// <summary>
        /// Draws the border for the control.
        /// </summary>
        /// <param name="g">
        /// The <see cref="Graphics"/> object used to draw the control.
        /// </param>
        private void DrawBorder(Graphics g)
        {
            Pen lightPen = null;
            Pen darkPen = null;
            SolidBrush backBrush = null;

            //Set initial color values.
            if (BackColor.Equals(SystemColors.ControlLightLight))
                lightPen = new Pen(SystemColors.ControlLight);
            else
                lightPen = new Pen(SystemColors.ControlLightLight);

            if (BackColor.Equals(SystemColors.ControlDark))
                darkPen = new Pen(SystemColors.ControlDarkDark);
            else
                darkPen = new Pen(SystemColors.ControlDark);

            //Fill in the background.
            backBrush = new SolidBrush(BackColor);
            g.FillRectangle(backBrush, 0, 0, Width, Height);
            backBrush.Dispose();

            switch (_borderStyle)
            {
                case PanelBorderStyle.None:
                    break;
                case PanelBorderStyle.Raised:
                    g.DrawLine(lightPen, 0, 0, Width, 0);
                    g.DrawLine(lightPen, 0, 0, 0, Height);
                    g.DrawLine(darkPen, Width - 1, 1, Width, Height);
                    g.DrawLine(darkPen, 1, Height - 1, Width, Height - 1);
                    break;
                case PanelBorderStyle.Inset:
                    g.DrawLine(darkPen, 0, 0, Width, 0);
                    g.DrawLine(darkPen, 0, 0, 0, Height);
                    g.DrawLine(lightPen, Width - 1, 1, Width, Height);
                    g.DrawLine(lightPen, 1, Height - 1, Width, Height - 1);
                    break;
                case PanelBorderStyle.ButtonRaised:
                    lightPen = new Pen(SystemColors.ControlLightLight);
                    darkPen = new Pen(SystemColors.ControlDarkDark);
                    g.DrawLine(lightPen, 0, 0, Width, 0);
                    g.DrawLine(lightPen, 0, 0, 0, Height);
                    g.DrawLine(darkPen, Width - 1, 1, Width - 1, Height);
                    g.DrawLine(darkPen, 1, Height - 1, Width, Height - 1);
                    lightPen.Dispose();
                    darkPen.Dispose();
                    lightPen = new Pen(SystemColors.ControlLightLight);
                    darkPen = new Pen(SystemColors.ControlDarkDark);
                    g.DrawLine(lightPen, 1, 1, Width - 2, 1);
                    g.DrawLine(lightPen, 1, 1, 1, Height - 2);
                    g.DrawLine(darkPen, Width - 2, 1, Width - 2, Height);
                    g.DrawLine(darkPen, 2, Height - 2, Width - 2, Height - 2);
                    break;
                case PanelBorderStyle.ButtonInset:
                    lightPen = new Pen(SystemColors.ControlLightLight);
                    darkPen = new Pen(SystemColors.ControlDarkDark);
                    g.DrawLine(darkPen, 0, 0, Width, 0);
                    g.DrawLine(darkPen, 0, 0, 0, Height);
                    g.DrawLine(lightPen, Width - 1, 1, Width, Height);
                    g.DrawLine(lightPen, 1, Height - 1, Width, Height - 1);
                    lightPen.Dispose();
                    darkPen.Dispose();
                    lightPen = new Pen(SystemColors.ControlLightLight);
                    darkPen = new Pen(SystemColors.ControlDarkDark);
                    g.DrawLine(darkPen, 1, 1, Width - 1, 1);
                    g.DrawLine(darkPen, 1, 1, 1, Height - 1);
                    g.DrawLine(lightPen, Width - 2, 1, Width - 2, Height);
                    g.DrawLine(lightPen, 2, Height - 2, Width - 3, Height - 2);
                    break;
            }
            lightPen.Dispose();
            darkPen.Dispose();

            lightPen = null;
            darkPen = null;
            backBrush = null;
        }
        /// <summary>
        /// Draws the gradient image.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> object used for drawing.</param>
        /// <param name="x">The left or X pixel at which to begin.</param>
        /// <param name="y">The top or Y pixel at which to begin.</param>
        /// <param name="width">The width of the new image.</param>
        /// <param name="height">The height of the new image.</param>
        private void DrawGradient(Graphics g, int x, int y, int width, int height)
        {
            if (_gradientStyle == BarStyle.FlatGradient)
                DrawFlatGradient(g, x, y, width, height);
            else
                DrawBevelGradient(g, x, y, width, height);
        }
        /// <summary>
        /// Draws a flat, horizontal, two-color gradient.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> object used for drawing.</param>
        /// <param name="x">The left or X pixel at which to begin.</param>
        /// <param name="y">The top or Y pixel at which to begin.</param>
        /// <param name="width">The width of the new image.</param>
        /// <param name="height">The height of the new image.</param>
        private void DrawFlatGradient(Graphics g, int x, int y, int width, int height)
        {
            LinearGradientBrush brush = null;
            Rectangle rect;
            Color startColor;
            Color endColor;

            startColor = Color.FromArgb(_startColor.A + _percentComplete,
                _startColor.R, _startColor.G, _startColor.B);

            endColor = Color.FromArgb(_endColor.A + _percentComplete, _endColor.R,
                _endColor.G, _endColor.B);

            rect = new Rectangle(x, y, CalculateAreaPercent(width) + 2, height);
            brush = new LinearGradientBrush(rect, startColor, endColor, LinearGradientMode.Horizontal);
            g.FillRectangle(brush, rect);
            brush.Dispose();
            brush = null;
        }
        /// <summary>
        /// Draws a bevel-style gradient.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> object used for drawing.</param>
        /// <param name="x">The left or X pixel at which to begin.</param>
        /// <param name="y">The top or Y pixel at which to begin.</param>
        /// <param name="width">The width of the new image.</param>
        /// <param name="height">The height of the new image.</param>
        private void DrawBevelGradient(Graphics g, int x, int y, int width, int height)
        {
            LinearGradientBrush brush = null;
            Rectangle topRect;
            Rectangle bottomRect;
            Color foreColor;
            Color fadeColor;
            int midHeight = 0;
            int midTop = 0;

            //Calculate values.
            width = CalculateAreaPercent(width) + 2;
            midHeight = (int)((float)height / 2) + 1;
            midTop = y + midHeight;

            topRect = new Rectangle(x, y, width, midHeight);
            bottomRect = new Rectangle(x, midTop, width, midHeight);

            foreColor = _startColor;
            fadeColor = Color.FromArgb(80, _startColor.R, _startColor.G, _startColor.B);

            brush = new LinearGradientBrush(topRect, fadeColor, foreColor, LinearGradientMode.Vertical);
            g.FillRectangle(brush, topRect);
            brush.Dispose();

            brush = new LinearGradientBrush(bottomRect, foreColor, fadeColor, LinearGradientMode.Vertical);
            g.FillRectangle(brush, bottomRect);
            brush.Dispose();
        }
        /// <summary>
        /// Calculates the area to be covered as specified by the percentage value.
        /// </summary>
        /// <param name="width">The width to be used as the 100% value.</param>
        /// <returns>
        /// The current percentage value of the specified width.
        /// </returns>
        private int CalculateAreaPercent(int width)
        {
            return (int)(((float)_percentComplete / 100) * width);
        }
        private int GetBorderDepth()
        {
            int returnValue = 0;

            switch (_borderStyle)
            {
                case PanelBorderStyle.None:
                    returnValue = 0;
                    break;
                case PanelBorderStyle.Inset:
                case PanelBorderStyle.Raised:
                    returnValue = 1;
                    break;
                case PanelBorderStyle.ButtonInset:
                case PanelBorderStyle.ButtonRaised:
                    returnValue = 2;
                    break;
            }

            return returnValue;
        }
        private Point GetBorderPoint()
        {
            Point returnValue = new Point(0, 0);

            switch (_borderStyle)
            {
                case PanelBorderStyle.None:
                    returnValue.X = 0;
                    returnValue.Y = 0;
                    break;
                case PanelBorderStyle.Inset:
                case PanelBorderStyle.Raised:
                    returnValue.X = 1;
                    returnValue.Y = 1;
                    break;
                case PanelBorderStyle.ButtonInset:
                case PanelBorderStyle.ButtonRaised:
                    returnValue.X = 2;
                    returnValue.Y = 2;
                    break;
            }

            return returnValue;
        }
        #endregion

    }
}
